package com.yfdice.android.bam;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import com.yfdice.android.bam.DeviceItem;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class MoniterService {
    // Debugging
    private static final String TAG = "MoniterService";
    private static final int MSG_TYPE_DISCONECTED = 1;
    private static final int MSG_SEND_MESSAGE_DEVICE = 2;
    private static final boolean D = true;

    // Unique UUID for this application
    private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    // Member fields
    private ArrayList<MoniterItem> mMoniterList;
    private final BluetoothAdapter mAdapter;
    private MoniterThread mMoniterThread;
    private MoniterHander mMsghandler;
    private static MoniterService mInstance;
    private int mWarningRSSI = Settings.def_MAX_RSSI;
    private long mScanPeriod = 1000;
    
    private Handler mTimeHandler = new Handler();  
    private Runnable runnable = new Runnable() {   	  
    	public void run() {  
			Message message = mMsghandler.obtainMessage(MSG_SEND_MESSAGE_DEVICE);
			mMsghandler.sendMessage(message);
			mTimeHandler.postDelayed(runnable, mScanPeriod);  
    	}   
    };  
    
    public MoniterService() {  
		mMoniterList = new ArrayList<MoniterItem>();
		mAdapter = BluetoothAdapter.getDefaultAdapter();
    }
    
    static public MoniterService getInstance()
    {
    	if(mInstance == null)
    		mInstance = new MoniterService();
    	return mInstance;
    }
    
    public ArrayList<MoniterItem> getMoniterList()
    {
    	return mMoniterList;
    }
    
    public void start(){
    	// 启动监控线程
    	mMoniterThread = new MoniterThread("moniterthread");
    	Looper looper = mMoniterThread.getLooper();
    	mMsghandler = new MoniterHander(looper);
    	mTimeHandler.postDelayed(runnable, mScanPeriod);
 
    }
    
    public Handler getMoiniterHandle()
    {
    	return mMsghandler;
    }
    
    private class MoniterHander extends Handler
    {
    	public MoniterHander(Looper looper)
    	{
    		super(looper);
    	}
    	
    	@Override
    	public void handleMessage(Message msg)
    	{
    		switch(msg.what)
    		{
    		case MSG_TYPE_DISCONECTED:
    			reconnect();
    			break;
    		case MSG_SEND_MESSAGE_DEVICE:
    			sendMessage2Device();
    		}
    	}
    	
    	private void reconnect()
    	{
    		for(MoniterItem moniterItem: mMoniterList)
    		{
    			// 判断当前连接和信号状态
    			if(moniterItem.item.nFilter != DeviceItem.FilterType_Always)
    			{
    				if(moniterItem.item.nState == DeviceItem.STATE_DISCONNECTED)
    				{
    					BluetoothDevice device = mAdapter.getRemoteDevice(moniterItem.item.strMAC);
    					moniterItem.connect(device);
    				}
    			}
    		}		
    	}
    	
    	private void sendMessage2Device()
    	{
    		write();	    		
    	}
    
    }

    public void setScanSchedule(long period)
    {
    	mScanPeriod = period;
    }
    
    public void setWarningRSSI(int rssi)
    {
    	mWarningRSSI = rssi;
    }
    
    public synchronized void stop() {
        if (D) Log.d(TAG, "stop");
        
        if(mMoniterThread != null)
        {
        	mMoniterThread.stopMoniter();
        }
        
        if(mMoniterList != null)
        {
        	// Cancel any thread currently running a connection
        	Iterator<MoniterItem> iter = mMoniterList.iterator();
        	while(iter.hasNext())
        	{
        		MoniterItem currentItem = iter.next();
        		if(currentItem != null)
        		{
        			currentItem.stop();
        		}	
        	}
        }
    }

    // 提供给启动恢复上次状态使用
    public synchronized void addDevice2Moniter(DeviceItem item){
        if (D) Log.d(TAG, "addDevice2Moniter");
        
        BluetoothDevice device = mAdapter.getRemoteDevice(item.strMAC);
        
        // 如已经存在列表，则修改对象启动监控线程
        for(MoniterItem moniterItem: mMoniterList)
        {
        	if(moniterItem.item.strMAC.equalsIgnoreCase(item.strMAC))
        	{
        		item.strName = device.getName();
        		item.nRSSI = 0;
        		moniterItem.connect(device);
        		return;
        	}
        }
        
        // 不存在就新建一个对象加入列表,然后启动
        MoniterItem objMoniter = new MoniterItem(device);
        mMoniterList.add(objMoniter);
        objMoniter.connect(device);
    }
    
    public synchronized void addDevice2Moniter(BluetoothDevice device){
        if (D) Log.d(TAG, "addDevice2Moniter(BluetoothDevice device)");
        
        // 不存在就新建一个对象加入列表,然后启动
        MoniterItem objMoniter = new MoniterItem(device);
        mMoniterList.add(objMoniter);
        objMoniter.connect(device);
    }
    
    public synchronized boolean isInMoniterList(DeviceItem item)
    {
        // 查询是否已经存在监控列表里面
        for(MoniterItem moniterItem: mMoniterList)
        {
        	if(moniterItem.item.strMAC.equalsIgnoreCase(item.strMAC))
        	{
        		return true;
        	}
        }
        
        return false;
    }
    
    public synchronized boolean isInMoniterList(String strMAC)
    {
        // 查询是否已经存在监控列表里面
        for(MoniterItem moniterItem: mMoniterList)
        {
        	if(moniterItem.item.strMAC.equalsIgnoreCase(strMAC))
        	{
        		return true;
        	}
        }
        
        return false;
    }
    
    public boolean hasDisconnectDevice()
    {	
    	if(mMoniterList == null)
    		return false;
    	
		for(MoniterItem moniterItem: mMoniterList)
		{
			// 判断当前连接和信号状态
			if(moniterItem.item.nFilter != DeviceItem.FilterType_Always)
			{
				if(moniterItem.item.nState == DeviceItem.STATE_DISCONNECTED ||
						moniterItem.item.nState == DeviceItem.STATE_CONNECTING)
					return true;
			}
		}    	
    	
    	return false;
    }
    
    public boolean hasLowSingalDevice()
    {	
    	if(mMoniterList == null)
    		return false;
    	
		for(MoniterItem moniterItem: mMoniterList)
		{
			// 判断当前连接和信号状态
			if(moniterItem.item.nFilter != DeviceItem.FilterType_Always)
			{	
				if(moniterItem.item.nRSSI >= mWarningRSSI)
					return true;
			}
		}
		
		return false;
    }

    /**
     * Write to the ConnectedThread in an unsynchronized manner
     * @param out The bytes to write
     * @see ConnectedThread#write(byte[])
     */
    public void write() {
        // Create temporary object
    	
    	if(mMoniterList == null)
    		return;
    	
    	MoniterItem objTemp;
		byte[] code = { (byte)0xAC, (byte)0x53,  (byte)0x21,  (byte)0x00,  (byte)0x88,  (byte)0xFF};
		
        // Cancel any thread currently running a connection
        Iterator<MoniterItem> iter = mMoniterList.iterator();
        while(iter.hasNext())
        {
        	objTemp = iter.next();
        	if(objTemp.isOnline())
        	{
        		String mac = objTemp.getMac();
        		String lap = mac.substring(15, 17);
        		code[3] = (byte)Integer.parseInt(lap, 16);
        		objTemp.write(code);
        	}
        }
    }
    
    public class MoniterItem
    {
    	public DeviceItem item;
        private ConnectThread mConnectThread;
        private ConnectedThread mConnectedThread;
    	
    	MoniterItem(DeviceItem argitem)
    	{
    		item = new DeviceItem(argitem);
    	}
    	
    	MoniterItem(BluetoothDevice device)
    	{
    		item = new DeviceItem();
    		item.strMAC = device.getAddress();
    		item.strName = device.getName();
    	}
    	
    	// 当断开的链接，重新尝试
    	void refreshDeviceInfo()
    	{		
    	}
    	
        private synchronized void setState(int state) {
            if (D) Log.d(TAG, "setState() " + item.nState + " -> " + state);
            item.nState = state;
            item.nRSSI = 0;
            item.nBattery = 0;
        }

        /**
         * Return the current connection state. */
        public synchronized int getState() {
            return item.nState;
        }
        
        public synchronized String getMac() {
        	return item.strMAC;
        }

        /**
         * Start the ConnectThread to initiate a connection to a remote device.
         * @param device  The BluetoothDevice to connect
         */
        public synchronized void connect(BluetoothDevice device) {
            if (D) Log.d(TAG, "connect to: " + item.strMAC);

            // Cancel any thread attempting to make a connection
            if (item.nState == DeviceItem.STATE_CONNECTING) {
                if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
            }

            // Cancel any thread currently running a connection
            if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}

            // Start the thread to connect with the given device
            mConnectThread = new ConnectThread(device);
            mConnectThread.start();
            setState(DeviceItem.STATE_CONNECTING);
        }
        
        public void stop()
        {
            // Cancel the thread that completed the connection
            if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}

            // Cancel any thread currently running a connection
            if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
        }

        /**
         * Start the ConnectedThread to begin managing a Bluetooth connection
         * @param socket  The BluetoothSocket on which the connection was made
         * @param device  The BluetoothDevice that has been connected
         */
        public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
            if (D) Log.d(TAG, "connected");

            // Cancel the thread that completed the connection
            if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}

            // Cancel any thread currently running a connection
            if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}

            // Start the thread to manage the connection and perform transmissions
            mConnectedThread = new ConnectedThread(socket);
            mConnectedThread.start();

            setState(DeviceItem.STATE_CONNECTED);
        }
        
        public boolean isOnline()
        {
        	return (item.nFilter != DeviceItem.FilterType_Always &&
        			item.nState == DeviceItem.STATE_CONNECTED);
        }
        
        public boolean isLowSingal()
        {
        	return (isOnline() && item.nRSSI >= mWarningRSSI);
        }

        /**
         * Write to the ConnectedThread in an unsynchronized manner
         * @param out The bytes to write
         * @see ConnectedThread#write(byte[])
         */
        public void write(byte[] out) {
            // Create temporary object
            ConnectedThread r;
            // Synchronize a copy of the ConnectedThread
            synchronized (this) {
                if (item.nState != DeviceItem.STATE_CONNECTED) 
                	return;
                r = mConnectedThread;
            }
            // Perform the write unsynchronized
            r.write(out);
        }

        /**
         * Indicate that the connection attempt failed and notify the UI Activity.
         */
        private void connectionFailed() {
            setState(DeviceItem.STATE_DISCONNECTED);
            MoniterHander handle = new MoniterHander(mMoniterThread.getLooper());
            Message msg = handle.obtainMessage(MSG_TYPE_DISCONECTED);
            handle.sendMessage(msg);
        }


        /**
         * This thread runs while attempting to make an outgoing connection
         * with a device. It runs straight through; the connection either
         * succeeds or fails.
         */
        private class ConnectThread extends Thread {
            private BluetoothSocket mmSocket;
            private final BluetoothDevice mmDevice;

            public ConnectThread(BluetoothDevice device) {
                mmDevice = device;
                BluetoothSocket tmp = null;

                // Get a BluetoothSocket for a connection with the
                // given BluetoothDevice
                try {
                    tmp = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
                } catch (IOException e) {
                    Log.e(TAG, "create() failed", e);
                }
                mmSocket = tmp;
            }

            public void run() {
                Log.i(TAG, "BEGIN mConnectThread");
                setName("ConnectThread");
                
                if(mmSocket == null)
                {
                	try{
                		sleep(1000);                	
                	}catch(InterruptedException e)
                	{}
                	
                	connectionFailed();
                	return;
                }

                // Always cancel discovery because it will slow down a connection
                mAdapter.cancelDiscovery();

                // Make a connection to the BluetoothSocket
                try {
                    // This is a blocking call and will only return on a
                    // successful connection or an exception
                    mmSocket.connect();
                } catch (IOException e) {
                    // Close the socket
                	if(mmSocket != null)
                	{
	                    try {
	                        mmSocket.close();
	                        mmSocket = null;
	                    } catch (IOException e2) {
	                        Log.e(TAG, "unable to close() socket during connection failure", e2);
	                    }
                	}
                    
                    connectionFailed();
                    return;
                }

                // Reset the ConnectThread because we're done
                synchronized (MoniterItem.this) {
                    mConnectThread = null;
                }

                // Start the connected thread
                connected(mmSocket, mmDevice);
            }

            public void cancel() {
            	if(mmSocket != null)
            	{
            		// 这里如果没关闭情况下会偶然出现卡线程情况, 后续可考虑异步post消息处理
	                try {
                		mmSocket.close();
                        mmSocket = null;
	                } catch (IOException e) {
	                    Log.e(TAG, "close() of connect socket failed", e);
	                }
            	}
            }
        }

        /**
         * This thread runs during a connection with a remote device.
         * It handles all incoming and outgoing transmissions.
         */
        private class ConnectedThread extends Thread {
            private BluetoothSocket mmSocket;
            private final InputStream mmInStream;
            private final OutputStream mmOutStream;

            public ConnectedThread(BluetoothSocket socket) {
                Log.d(TAG, "create ConnectedThread");
                mmSocket = socket;
                InputStream tmpIn = null;
                OutputStream tmpOut = null;

                // Get the BluetoothSocket input and output streams
                try {
                    tmpIn = socket.getInputStream();
                    tmpOut = socket.getOutputStream();
                } catch (IOException e) {
                    Log.e(TAG, "temp sockets not created", e);
                }

                mmInStream = tmpIn;
                mmOutStream = tmpOut;
            }

            public void run() {
                Log.i(TAG, "BEGIN mConnectedThread");
                byte[] buffer = new byte[64];
                int bytes;
                float fRssi = 0;

                // Keep listening to the InputStream while connected
                while (true) {
                    try {
                        // Read from the InputStream
                        bytes = mmInStream.read(buffer);
                        // 获取RSSI信号做判断等处理
                        fRssi = Math.abs(buffer[3]);
                        item.nBattery = Math.abs(buffer[4]);
                        
                        // 滤波
                        if(item.nRSSI == 0)
                        {
                        	item.nRSSI = (int)fRssi;
                        }
                        else
                        {
                        	item.nRSSI = (int)(item.nRSSI*0.7 + fRssi*0.3);
                        }
                        
                        Log.i(TAG, String.valueOf(item.nRSSI));
                    } catch (IOException e) {
                        Log.e(TAG, "disconnected", e);
                        cancel();
                        connectionFailed();
                        break;
                    }
                }
                
                mConnectedThread = null;
            }

            /**
             * Write to the connected OutStream.
             * @param buffer  The bytes to write
             */
            public void write(byte[] buffer) {
                try {
                    mmOutStream.write(buffer);
                } catch (IOException e) {
                    Log.e(TAG, "Exception during write", e);
                    connectionFailed();
                }
            }

            public void cancel() {
            	if(mmSocket != null)
            	{
	                try {
	                    mmSocket.close();
	                    mmSocket = null;               		
	                } catch (IOException e) {
	                    Log.e(TAG, "close() of connect socket failed", e);
	                }
            	}
            }
        }        	
    }
    
    /**
    *  负责定期写发生心跳信号和处理反馈回来的message
    */
    private class MoniterThread implements Runnable{
    	private final Object mLock = new Object();
    	private Looper mLooper;
    	
    	MoniterThread(String threadName)
    	{
    		Thread t = new Thread(null,  this, threadName);
    		t.start();
    		synchronized(mLock)
    		{
    			while(mLooper == null)
    			{
    				try{
    					mLock.wait();    					
    				}catch(InterruptedException e){
    					break;
    				}
    				
    			}
    		}
    	}
    	
    	public Looper getLooper()
    	{
    		return mLooper;
    	}
    	
    	public void stopMoniter()
    	{
    		mLooper.quit();
    	}
    	
    	public void run()
    	{
    		// 创建消息的Looper
    		synchronized (mLock) {
				Looper.prepare();
				mLooper = Looper.myLooper();
				mLock.notifyAll();
			}
    		Looper.loop();	
    	}
    }
    
}
